/*=========================================================================

     game.cpp  -  Copyright (C) 1993-2005 by Don Cross

     Contains class ChessGame.

=========================================================================*/

#include <string.h>
#include <stdio.h>

#include "chess.h"
#include "gamefile.h"
#include "lrntree.h"


bool ReadFromFile ( FILE *f, Move &m )
{
    int data [4];

    data[0] = fgetc(f);
    data[1] = fgetc(f);
    data[2] = fgetc(f);
    data[3] = fgetc(f);

    if ( data[3] == EOF )
        return false;

    m.source = BYTE (data[0]);
    m.dest   = BYTE (data[1]);
    m.score  = INT16 (data[2] | (data[3] << 8)); 

    return true;   
}


bool WriteToFile ( FILE *f, const Move &m )
{
    if ( fputc(m.source,f) == EOF )
        return false;

    if ( fputc(m.dest,f) == EOF )
        return false;

    if ( fputc(m.score & 0xff, f) == EOF )
        return false;

    if ( fputc(m.score >> 8, f) == EOF )
        return false;

    return true; 
}


//------------------------------------------------------------------


ChessGame::ChessGame ( ChessBoard &Board, ChessUI &Ui ):
    board ( Board ),
    ui ( Ui ),
    autoSave_Filename ( 0 )
{
    whitePlayer = ui.CreatePlayer ( SIDE_WHITE );
    blackPlayer = ui.CreatePlayer ( SIDE_BLACK );
}

ChessGame::ChessGame ( 
    ChessBoard &_board, 
    ChessUI &_ui, 
    bool _whiteIsHuman, 
    bool _blackIsHuman,
    ChessPlayer *_whitePlayer,
    ChessPlayer *_blackPlayer ):
        whitePlayer ( _whitePlayer ),
        blackPlayer ( _blackPlayer ),
        board ( _board ),
        ui ( _ui ),
        autoSave_Filename ( 0 )
{
}


ChessGame::~ChessGame()
{
    if ( autoSave_Filename )
    {
        delete[] autoSave_Filename;
        autoSave_Filename = 0;
    }

    if ( whitePlayer )
    {
        delete whitePlayer;
        whitePlayer = 0;
    }

    if ( blackPlayer )
    {
        delete blackPlayer;
        blackPlayer = 0;
    }
}


bool ChessGame::AutoSaveToFile ( const char *filename )
{
    if ( autoSave_Filename )
        delete[] autoSave_Filename;

    if ( !filename )
    {
        // Interpret this as 'stop saving to file'
        autoSave_Filename = 0;
    }
    else
    {
        autoSave_Filename = new char [ strlen(filename) + 1 ];
        if ( autoSave_Filename )
            strcpy ( autoSave_Filename, filename );
        else
            return false;   // out of memory!
    }

    return true;
}


void ChessGame::DetectAutoSaveFile()
{
    // Undocumented feature that helps me gather game listings for tree trainer...
    // If the file game.counter exists, open it, scan for game number, increment,
    // and make auto-save to the file <counter>.pgn...
    const char *CounterFileName = "game.counter";
    FILE *file = fopen (CounterFileName, "rt");
    if (file) {
        int counter;
        if (1 == fscanf (file, "%d", &counter)) {
            char GameFileName [64];
            sprintf (GameFileName, "%05d.pgn", counter);
            if (AutoSaveToFile (GameFileName)) {
                fclose (file);
                file = fopen (CounterFileName, "wt");
                if (file) {
                    fprintf (file, "%d\n", ++counter);
                }
            }
        }

        if (file) {
            fclose (file);
            file = NULL;
        }
    }
}


void ChessGame::Play()
{
    if ( sizeof(INT16) != 2 || sizeof(INT32) != 4 )
    {
        // It's OK if compiler reports an "unreachable code" warning...
        ChessFatal ( "Integer types are wrong size!" );
    }

    MoveList     ml;
    Move         move;
    UnmoveInfo   unmove;

    if ( !whitePlayer || !blackPlayer )
        ChessFatal ( "Chess player(s) not initialized!" );

    move.dest = SPECIAL_MOVE_NULL;   // to assist in checking for board edit

    for(;;)
    {
        ui.DrawBoard ( board );

        if (board.GetCurrentPlyNumber() == 0) {     // handle starting multiple games
            DetectAutoSaveFile();   // every time we start a new game, increment game number
        } else {
            if (autoSave_Filename) {
                SaveGamePGN ( board, autoSave_Filename );
            }
        }

        if ( board.WhiteToMove() )
        {
            board.GenWhiteMoves ( ml );
            if ( ml.num == 0 )
            {
                // The game is over...

                whitePlayer->InformGameOver (board);
                ChessSide winner = board.WhiteInCheck() ? SIDE_BLACK : SIDE_NEITHER;
                ui.ReportEndOfGame ( winner );
                LearnTree tree;
                tree.learnFromGame ( board, winner );
                return;
            }
            else if ( !move.ShouldIgnore() && board.IsDefiniteDraw() )
            {
                whitePlayer->InformGameOver (board);
                ui.ReportEndOfGame ( SIDE_NEITHER );
                LearnTree tree;
                tree.learnFromGame ( board, SIDE_NEITHER );
                return;
            }

            INT32 timeSpent = 0;
            if ( !whitePlayer->GetMove ( board, move, timeSpent ) )
            {
                blackPlayer->InformResignation();
                ui.Resign ( SIDE_WHITE, whitePlayer->QueryQuitReason() );
                LearnTree tree;
                tree.learnFromGame ( board, SIDE_BLACK );
                return;
            }

            if ( !move.ShouldIgnore() )
            {
                ui.RecordMove ( board, move, timeSpent );
                board.MakeWhiteMove ( move, unmove, true, true );
            }
        }
        else   // Black's turn to move...
        {
            board.GenBlackMoves ( ml );
            if ( ml.num == 0 )
            {
                // The game is over...

                blackPlayer->InformGameOver (board);
                ChessSide winner = board.BlackInCheck() ? SIDE_WHITE : SIDE_NEITHER;
                ui.ReportEndOfGame ( winner );
                LearnTree tree;
                tree.learnFromGame ( board, winner );
                return;
            }
            else if ( !move.ShouldIgnore() && board.IsDefiniteDraw() )
            {
                blackPlayer->InformGameOver (board);
                ui.ReportEndOfGame ( SIDE_NEITHER );
                LearnTree tree;
                tree.learnFromGame ( board, SIDE_NEITHER );
                return;
            }

            INT32 timeSpent = 0;
            if ( !blackPlayer->GetMove ( board, move, timeSpent ) )
            {
                whitePlayer->InformResignation();
                ui.Resign ( SIDE_BLACK, blackPlayer->QueryQuitReason() );
                LearnTree tree;
                tree.learnFromGame ( board, SIDE_WHITE );
                return;
            }

            if ( !move.ShouldIgnore() )
            {
                ui.RecordMove ( board, move, timeSpent );
                board.MakeBlackMove ( move, unmove, true, true );
            }
        }
    }
}


bool SaveGame ( const ChessBoard &board, const char *filename )
{
    FILE *gameFile = fopen ( filename, "wb" );
    if ( gameFile )
    {
        UINT32 n = board.GetCurrentPlyNumber();
        for ( UINT32 i=0; i < n; i++ )
        {
            Move m = board.GetPastMove(i);
            WriteToFile ( gameFile, m );
        }
        fclose ( gameFile );
    }
    else
        return false;

    return true;
}



bool LoadGame ( ChessBoard &board, const char *filename )
{
    FILE *gameFile = fopen ( filename, "rb" );
    if ( gameFile )
    {
        board.Init();
        Move move;
        for ( UINT32 i=0; ReadFromFile(gameFile,move); ++i )
        {
            if ( (move.dest & SPECIAL_MOVE_MASK) == SPECIAL_MOVE_EDIT )
            {
                board.EditCommand ( move );
                board.SaveSpecialMove ( move );
            }
            else
            {
                UnmoveInfo unmove;
                board.MakeMove ( move, unmove );
            }
        }
        fclose ( gameFile );
    }
    else
        return false;

    return true;
}
